package pl.smath.expression.parser.mathml;

import com.google.gwt.junit.client.GWTTestCase;
import pl.smath.expression.model.DivisionTerm;
import pl.smath.expression.model.ExponentiationTerm;
import pl.smath.expression.model.FractionTerm;
import pl.smath.expression.model.FunctionTerm;
import pl.smath.expression.model.NumberTerm;
import pl.smath.expression.model.ProductTerm;
import pl.smath.expression.model.RelationTerm;
import pl.smath.expression.model.RootTerm;
import pl.smath.expression.model.SetTerm;
import pl.smath.expression.model.SumTerm;
import pl.smath.expression.model.Term;
import pl.smath.expression.model.VariableTerm;
import pl.smath.expression.model.presentation.AbsoluteTerm;
import pl.smath.expression.model.presentation.AtomTerm;
import pl.smath.expression.model.presentation.ConjugateTerm;
import pl.smath.expression.model.presentation.ExtendedRelationTerm;
import pl.smath.expression.model.presentation.FactorialTerm;
import pl.smath.expression.model.presentation.FactorofTerm;
import pl.smath.expression.model.presentation.FunctionMappingTerm;
import pl.smath.expression.model.presentation.GapTerm;
import pl.smath.expression.model.presentation.IntervalTerm;
import pl.smath.expression.model.presentation.SubscriptTerm;
import pl.smath.expression.parser.ExpressionParserException;
import pl.smath.expression.parser.mathml.MathMLParser;

public class MathMLParserGwtTest extends GWTTestCase {

	private class Query{
		
		private String query;
		private String resultString;
		private Term resultTerm;
		
		public Query(String query, Term resultTerm, String resultString){
			this.query = query;
			this.resultString = resultString;
			this.resultTerm = resultTerm;
		}

		public String getQuery() {
			return query;
		}

		public String getResultString() {
			return resultString;
		}

		public Term getResultTerm() {
			return resultTerm;
		}
	};

	private SumTerm getTestedSumTerm(){
		SumTerm t = new SumTerm();
		t.addTerm(new NumberTerm("1"));
		t.addTerm(new NumberTerm("2"));
		return t;
	}
	
	private ProductTerm getTestedProductTerm(){
		ProductTerm t = new ProductTerm();
		t.addTerm(new NumberTerm("1"));
		t.addTerm(new NumberTerm("2"));
		return t;
	}

	private DivisionTerm getTestedDivisionTerm(){
		DivisionTerm t = new DivisionTerm();
		t.addTerm(new NumberTerm("1"));
		t.addTerm(new NumberTerm("2"));
		return t;
	}

	private SetTerm getTestedSetTerm(){
		SetTerm t = new SetTerm();
		t.addTerm(new VariableTerm("x"));
		t.addTerm(new VariableTerm("y"));
		t.addTerm(new VariableTerm("z"));
		return t;
	}
	
	private final Query[] queries = { 
			new Query("<math><cn>1</cn></math>", new NumberTerm("1"), "1"),
			new Query("<math><ci>z</ci></math>", new VariableTerm("z"), "z"),
			new Query("<math><msub><cn>1</cn><cn>2</cn></msub></math>", new SubscriptTerm(new NumberTerm("1"), new NumberTerm("2")), "1_2"),
			new Query("<math><msup><cn>1</cn><cn>2</cn></msup></math>", new ExponentiationTerm(new NumberTerm("1"), new NumberTerm("2")), "1^2"),
			new Query("<math><msubsup><cn>1</cn><cn>2</cn><cn>3</cn></msubsup></math>", new AtomTerm(new NumberTerm("1"), new NumberTerm("2"), new NumberTerm("3")), "1_2^3"),
			new Query("<math><interval closure='open-closed'><cn>1</cn><cn>2</cn></interval></math>", new IntervalTerm("open-closed", new NumberTerm("1"), new NumberTerm("2")), "(1,2>"),
			new Query("<math><gap/></math>", new GapTerm(), " "),
			new Query("<math><apply><plus/><cn>1</cn><cn>2</cn></apply></math>", getTestedSumTerm(), "1+2"),
			new Query("<math><apply><times/><cn>1</cn><cn>2</cn></apply></math>", getTestedProductTerm(), "1*2"),
			new Query("<math><apply><divide/><cn>1</cn><cn>2</cn></apply></math>", getTestedDivisionTerm(), "1:2"),
			new Query("<math><apply><sin/><cn>1</cn></apply></math>", new FunctionTerm("sin", new NumberTerm("1")), "sin(1)"),
			new Query("<math><apply><cos/><cn>1</cn></apply></math>", new FunctionTerm("cos", new NumberTerm("1")), "cos(1)"),
			new Query("<math><apply><ln/><cn>1</cn></apply></math>", new FunctionTerm("ln", new NumberTerm("1")), "ln(1)"),
			new Query("<math><apply><log/><cn>1</cn></apply></math>", new FunctionTerm("log", new NumberTerm("1")), "log(1)"),
			new Query("<math><apply><factorial/><cn>1</cn></apply></math>", new FactorialTerm(new NumberTerm("1")), "1!"),
			new Query("<math><apply><power/><cn>1</cn><cn>2</cn></apply></math>", new ExponentiationTerm(new NumberTerm("1"),new NumberTerm("2")), "1^2"),
			new Query("<math><apply><root/><cn>2</cn><cn>1</cn></apply></math>", new RootTerm(new NumberTerm("1"),new NumberTerm("2")), "1$2"),
			new Query("<math><apply><abs/><cn>1</cn></apply></math>", new AbsoluteTerm(new NumberTerm("1")), "|1|"),
			new Query("<math><apply><conjugate/><cn>1</cn></apply></math>", new ConjugateTerm(new NumberTerm("1")), "1*"),
			new Query("<math><apply><fraction/><cn>1</cn><cn>2</cn></apply></math>", new FractionTerm(new NumberTerm("1"),new NumberTerm("2")), "1/2"),
			new Query("<math><apply><approx/><cn>1</cn><cn>2</cn></apply></math>", new ExtendedRelationTerm("~=",new NumberTerm("1"),new NumberTerm("2")), "1~=2"),
			new Query("<math><apply><factorof/><cn>1</cn><cn>2</cn></apply></math>", new FactorofTerm("factorof",new NumberTerm("1"),new NumberTerm("2")), "1|2"),
			new Query("<math><apply><notfactorof/><cn>1</cn><cn>2</cn></apply></math>", new FactorofTerm("notfactorof",new NumberTerm("1"),new NumberTerm("2")), "1!|2"),
			new Query("<apply><functionmap/><ci>f</ci><ci>x</ci><ci>y</ci></apply>", new FunctionMappingTerm(new VariableTerm("f"), new VariableTerm("x"), new VariableTerm("y")), "f:x-y"),
			new Query("<apply><eq/><ci>a</ci><ci>b</ci></apply>", new RelationTerm("=", new VariableTerm("a"), new VariableTerm("b")), "a=b"),
			new Query("<apply><neq/><ci>a</ci><ci>b</ci></apply>", new RelationTerm("!=", new VariableTerm("a"), new VariableTerm("b")), "a!=b"),
			new Query("<apply><gt/><ci>a</ci><ci>b</ci></apply>", new RelationTerm(">", new VariableTerm("a"), new VariableTerm("b")), "a>b"),
			new Query("<apply><geq/><ci>a</ci><ci>b</ci></apply>", new RelationTerm(">=", new VariableTerm("a"), new VariableTerm("b")), "a>=b"),
			new Query("<apply><lt/><ci>a</ci><ci>b</ci></apply>", new RelationTerm("<", new VariableTerm("a"), new VariableTerm("b")), "a<b"),
			new Query("<apply><leq/><ci>a</ci><ci>b</ci></apply>", new RelationTerm("<=", new VariableTerm("a"), new VariableTerm("b")), "a<=b"),
			new Query("<apply><identical/><ci>a</ci><ci>b</ci></apply>", new ExtendedRelationTerm("===", new VariableTerm("a"), new VariableTerm("b")), "a===b"),
			new Query("<apply><equivalent/><ci>a</ci><ci>b</ci></apply>", new ExtendedRelationTerm("~", new VariableTerm("a"), new VariableTerm("b")), "a~b"),
			new Query("<apply><approx/><ci>a</ci><ci>b</ci></apply>", new ExtendedRelationTerm("~=", new VariableTerm("a"), new VariableTerm("b")), "a~=b"),
			new Query("<apply><isomorphic/><ci>a</ci><ci>b</ci></apply>", new ExtendedRelationTerm("~==", new VariableTerm("a"), new VariableTerm("b")), "a~==b"),
			new Query("<apply><orthogonal/><ci>a</ci><ci>b</ci></apply>", new ExtendedRelationTerm("_|_", new VariableTerm("a"), new VariableTerm("b")), "a_|_b"),
			new Query("<apply><parallel/><ci>a</ci><ci>b</ci></apply>", new ExtendedRelationTerm("||", new VariableTerm("a"), new VariableTerm("b")), "a||b"),
			new Query("<set><ci>x</ci><ci>y</ci><ci>z</ci></set>", getTestedSetTerm(), "{x, y, z}")
	};
	
	public void testTerms() throws ExpressionParserException{
		Term t;
		MathMLParser mmlParser = new MathMLParser();
		Integer q = 0;
		try {
			for (; q < queries.length ; q ++){
				t = mmlParser.parse(queries[q].getQuery());
				assertTrue("Failed iteration " + q.toString() + " " + queries[q].getQuery(), t.isAbsTheSame(queries[q].getResultTerm()));
			}
		} catch (Exception e) {
			fail("Failed iteration " + q.toString() + " " + queries[q].getQuery());
		}
	}
	
	public void testStrings() throws ExpressionParserException{
		Term t;
		MathMLParser mmlParser = new MathMLParser();
		Integer q = 0;
		try {
			for (; q < queries.length ; q ++){
				t = mmlParser.parse(queries[q].getQuery());
				assertTrue("Failed iteration " + q.toString() + " " + queries[q].getQuery(), t.toString().equals(queries[q].getResultString()));
			}
		} catch (Exception e) {
			fail("Failed iteration " + q.toString() + " " + queries[q].getQuery());
		}
	}
	

	@Override
	public String getModuleName() {
		return "pl.smath.expression.Expression";
	}
	
}
